#if 0  // 一个cdev对象对应多个设备节点
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kfifo.h>

#define VSER_MAJOR    256
#define VSER_MINOR    0
#define VSER_DEV_CNT  2
#define VSER_DEV_NAME "vser"

static struct cdev vsdev;
DEFINE_KFIFO(vsfifo0, char, 1024);
DEFINE_KFIFO(vsfifo1, char, 1024);

static int vser_open(struct inode* inode, struct file *filp)
{
    switch (MINOR(inode->i_rdev)) {
    default:
    case 0:
        filp->private_data = &vsfifo0;
        break;
    case 1:
        filp->private_data = &vsfifo1;
        break;
    }
    return 0;
}

static int vser_release(struct inode* inode, struct file *filp)
{
    return 0;
}

static ssize_t vser_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
    unsigned int copied = 0;
    int ret;
    struct kfifo *vsfifo = filp->prevate_data;
    printk("<---%ld---\n", count);
    ret = kfifo_to_user(vsfifo, buf, count, &copied);  // 把FIFO中的数据返回给用户层
    return copied;
}

static ssize_t vser_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
    unsigned int copied = 0;
    int ret;
    struct kfifo *vsfifo = filp->prevate_data;
    printk("---%ld--->\n", count);
    ret = kfifo_from_user(vsfifo, buf, count, &copied);  // 从用户层中读取数据到FIFO
    return copied;
}

static struct file_operations vser_ops = {  // 操作字符设备的一些方法，将函数指针指向定义的函数
    .owner = THIS_MODULE,
    .open = vser_open,
    .release = vser_release,  // 文件可以被打开很多次，但只有到最后一个close操作才会导致.release函数被调用
    .read = vser_read,
    .write = vser_write,
};

static int __init vser_init(void)
{
    int ret;
    dev_t dev;

    dev = MKDEV(VSER_MAJOR, VSER_MINOR);
    ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME);
    if (ret)
        goto reg_err;

    cdev_init(&vsdev, &vser_ops);
    vsdev.owner = THIS_MODULE;

    ret = cdev_add(&vsdev, dev, VSER_DEV_CNT);
    if (ret)
        goto add_err;

    return 0;
add_err:
    unregister_chrdev_region(dev, VSER_DEV_CNT);
reg_err:
    return ret;
}

static void __exit vser_exit(void)
{
    dev_t dev;
    dev = MKDEV(VSER_MAJOR, VSER_MINOR);

    cdev_del(&vsdev);
    unregister_chrdev_region(dev, VSER_DEV_CNT);
}

module_init(vser_init);
module_exit(vser_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("KDY <chaoxi_todo@qq.com>");
MODULE_DESCRIPTION("A simple module");
MODULE_ALIAS("virtual-serial");

#else  // 多个cdev对象对应多个设备节点

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kfifo.h>

#define VSER_MAJOR    256
#define VSER_MINOR    0
#define VSER_DEV_CNT  2
#define VSER_DEV_NAME "vser"

DEFINE_KFIFO(vsfifo0, char, 1024);
DEFINE_KFIFO(vsfifo1, char, 1024);

struct vser_dev {
    struct kfifo *fifo;
    struct cdev cdev;
};

static struct vser_dev vsdev[2];

static int vser_open(struct inode* inode, struct file *filp)
{
    // container_if: 根据结构体成员的地址反向得到结构体的起始地址
    filp->private_data = container_of(inode->i_cdev, struct vser_dev, cdev);
    return 0;
}

static int vser_release(struct inode* inode, struct file *filp)
{
    return 0;
}

static ssize_t vser_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
    unsigned int copied = 0;
    int ret;
    struct vser_dev *dev = filp->private_data;
    ret = kfifo_to_user(dev->fifo, buf, count, &copied);  // 把FIFO中的数据返回给用户层
    return copied;
}

static ssize_t vser_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
    unsigned int copied = 0;
    int ret;
    struct vser_dev *dev = filp->private_data;
    ret = kfifo_from_user(dev->fifo, buf, count, &copied);  // 从用户层中读取数据到FIFO
    return copied;
}

static struct file_operations vser_ops = {  // 操作字符设备的一些方法，将函数指针指向定义的函数
    .owner = THIS_MODULE,
    .open = vser_open,
    .release = vser_release,  // 文件可以被打开很多次，但只有到最后一个close操作才会导致.release函数被调用
    .read = vser_read,
    .write = vser_write,
};

static int __init vser_init(void)
{
    int ret, i;
    dev_t dev;

    dev = MKDEV(VSER_MAJOR, VSER_MINOR);
    ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME);
    if (ret)
        goto reg_err;

    for (i = 0; i < VSER_DEV_CNT; i++)
    {
        cdev_init(&vsdev[i].cdev, &vser_ops);
        vsdev[i].cdev.owner = THIS_MODULE;
        vsdev[i].fifo = (i == 0 ? (struct kfifo *)&vsfifo0 : (struct kfifo *)&vsfifo1);

        ret = cdev_add(&vsdev[i].cdev, dev+i, 1);
        if (ret)
            goto add_err;
    }

    return 0;
add_err:
    for (--i; i > 0; --i)
        cdev_del(&vsdev[i].cdev);
    unregister_chrdev_region(dev, VSER_DEV_CNT);
reg_err:
    return ret;
}

static void __exit vser_exit(void)
{
    int i;
    dev_t dev;
    dev = MKDEV(VSER_MAJOR, VSER_MINOR);

    for (i = 0; i < VSER_DEV_CNT; ++i)
        cdev_del(&vsdev[i].cdev);
    unregister_chrdev_region(dev, VSER_DEV_CNT);
}

module_init(vser_init);
module_exit(vser_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("KDY <chaoxi_todo@qq.com>");
MODULE_DESCRIPTION("A simple module");
MODULE_ALIAS("virtual-serial");
#endif